// import com.alibaba.druid.DbType;
// import com.alibaba.druid.sql.ast.SQLExpr;
// import com.alibaba.druid.sql.ast.SQLHint;
// import com.alibaba.druid.sql.ast.SQLReplaceable;
// import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
// import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor;
// import com.alibaba.druid.sql.visitor.SQLASTVisitor;

// import java.util.ArrayList;
// import java.util.List;

#include "OracleUpdateStatement.h"
#include "../../../../../DbType.h"
#include "../../../../ast/SQLExpr.h"
#include "../../../../ast/SQLHint.h"
#include "../../../../ast/statement/SQLTableSource.h"
#include "../../../../ast/statement/SQLWithSubqueryClause.h"
#include "../../../../ast/statement/SQLUpdateSetItem.h"
#include "../../../../visitor/SQLASTVisitor.h"
#include "../../visitor/OracleASTVisitor.h"
#include "../../../../../utils/instanceof.h"
#include "../../../../../utils/listTran.h"
#include "../../../../../utils/ifeq.h"

OracleUpdateStatement::OracleUpdateStatement()
    : SQLUpdateStatement(DbType_ptr(new DbType(&DbType::oracle)))
{
  hints = std::make_shared<std::list<SQLHint_ptr>>();
  returningInto = std::make_shared<std::list<SQLExpr_ptr>>();
}

SQLExpr_list_ptr OracleUpdateStatement::getReturningInto()
{
  return returningInto;
}

void OracleUpdateStatement::addReturningInto(SQLExpr_ptr returningInto)
{
  if (returningInto == nullptr)
  {
    return;
  }
  returningInto->setParent(SharedObject(OracleUpdateStatement));
  this->returningInto->push_back(returningInto);
}

// @Override
void OracleUpdateStatement::accept0(SQLASTVisitor_ptr visitor)
{
  if (instanceof <SQLASTVisitor, OracleASTVisitor>(visitor))
  {
    accept0(std::dynamic_pointer_cast<OracleASTVisitor>(visitor));
    return;
  }

  SQLUpdateStatement::accept(visitor);
}

// @Override
void OracleUpdateStatement::accept0(OracleASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(OracleUpdateStatement)))
  {
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLHint, SQLObject>(this->hints);
    SQLObjectImpl::acceptChild(visitor, tmp1);
    SQLObjectImpl::acceptChild(visitor, tableSource);
    SQLObject_list_ptr tmp2 = ListTran::tran_class<SQLUpdateSetItem, SQLObject>(items);
    SQLObjectImpl::acceptChild(visitor, tmp2);
    SQLObjectImpl::acceptChild(visitor, where);
    SQLObject_list_ptr tmp3 = ListTran::tran_class<SQLExpr, SQLObject>(returning);
    SQLObjectImpl::acceptChild(visitor, tmp3);
    SQLObject_list_ptr tmp4 = ListTran::tran_class<SQLExpr, SQLObject>(returningInto);
    SQLObjectImpl::acceptChild(visitor, tmp4);
  }
  visitor->endVisit(SharedObject(OracleUpdateStatement));
}

// @Override
BOOL_ptr OracleUpdateStatement::replace(SQLExpr_ptr expr, SQLExpr_ptr target)
{
  BOOL_ptr replace = SQLUpdateStatement::replace(expr, target);
  if (replace)
  {
    return BOOL::TRUE;
  }

  auto it_returningInto = returningInto->begin();
  for (int i = returningInto->size() - 1; i >= 0; i--)
  {
    std::advance(it_returningInto, i);
    if (*it_returningInto == expr)
    {
      target->setParent(SharedObject(OracleUpdateStatement));
      *it_returningInto = target;
      return BOOL::TRUE;
    }
  }

  return BOOL::FALSE;
}

string_ptr OracleUpdateStatement::getAlias()
{
  return this->alias;
}

void OracleUpdateStatement::setAlias(string_ptr alias)
{
  this->alias = alias;
}

BOOL_ptr OracleUpdateStatement::isOnly()
{
  return this->only;
}

void OracleUpdateStatement::setOnly(BOOL_ptr only)
{
  this->only = only;
}

int OracleUpdateStatement::getHintsSize()
{
  if (hints == nullptr)
  {
    return 0;
  }

  return hints->size();
}

SQLHint_list_ptr OracleUpdateStatement::getHints()
{
  if (hints == nullptr)
  {
    hints = std::make_shared<std::list<SQLHint_ptr>>();
  }
  return hints;
}

void OracleUpdateStatement::setHints(SQLHint_list_ptr hints)
{
  this->hints = hints;
}

// @Override
bool OracleUpdateStatement::equals(Object_ptr o)
{
  if (this == o.get())
  {
    return true;
  }
  if (o == nullptr || getClass()->c_str() != o->getClass()->c_str())
  {
    return false;
  }

  OracleUpdateStatement_ptr that = std::dynamic_pointer_cast<OracleUpdateStatement>(o);

  if (with != nullptr ? !with->equals(that->getWith()) : that->getWith() != nullptr)
  {
    return false;
  }
  if (!IFEQ::ifeq<SQLUpdateSetItem_ptr>(items, that->getItems()))
  {
    return false;
  }
  if (where != nullptr ? !where->equals(that->getWhere()) : that->getWhere() != nullptr)
  {
    return false;
  }
  if (from != nullptr ? !from->equals(that->getFrom()) : that->getFrom() != nullptr)
  {
    return false;
  }
  if (hints != nullptr ? !IFEQ::ifeq<SQLHint_ptr>(hints, that->hints) : that->hints != nullptr)
  {
    return false;
  }
  if (tableSource != nullptr ? !tableSource->equals(that->tableSource) : that->tableSource != nullptr)
  {
    return false;
  }
  if (returning != nullptr ? !IFEQ::ifeq<SQLExpr_ptr>(returning, that->returning) : that->returning != nullptr)
  {
    return false;
  }
  return orderBy != nullptr ? orderBy->equals(that->orderBy) : that->orderBy == nullptr;
}

// @Override
int OracleUpdateStatement::hashCode()
{
  int result = with != nullptr ? with->hashCode() : 0;
  result = 31 * result + typeid(*items).hash_code(); // items->hashCode();
  result = 31 * result + (where != nullptr ? where->hashCode() : 0);
  result = 31 * result + (from != nullptr ? from->hashCode() : 0);
  result = 31 * result + (tableSource != nullptr ? tableSource->hashCode() : 0);
  result = 31 * result + (returning != nullptr ? typeid(*returning).hash_code() /*returning->hashCode()*/ : 0);
  result = 31 * result + (orderBy != nullptr ? orderBy->hashCode() : 0);
  result = 31 * result + (hints != nullptr ? typeid(*hints).hash_code() /*hints->hashCode()*/ : 0);
  return result;
}
